サーバーサイドエンジニアがじっくり学ぶVue.jsチュートリアル【2. Introduction】
こんにちは。DA事業本部の春田です。
普段の業務では全く扱わないフロントエンドの世界に飛び込んでみたいと思います。最近3系が登場しましたがまだ情報が少ないので、2系のチュートリアルを進めていきます。サンドウィッチマンでいう「ちょっと何言ってるかわからない」ポイントは、その都度調べていきます。英語版の公式ドキュメントがベースです。
今回はIntroduction編です。
Vue.js とは?
Vue.jsは、ユーザーインターフェース(UI)を構築するためのプログレッシブ・フレームワークです。
Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable.
このモノシリックなフレームワークが何を指しているかといえば、例えばAngular.jsです。モノシリックなフレームワークはWeb開発に必要な機能が一通り含まれているため、開発の立ち上がりは早いですが、機能間の依存関係が深いので、規模が大きくなるにつれて扱いが難しくなってきます。Vue.jsはそれとは対象的なモジュラー型のフレームワークで、View層を中心としたライブラリが揃っています。疎結合で機能の追加がしやすく、既存のWebアプリにも導入しやすい分、一から開発する場合には必要なライブラリを自分で調達する必要があります。
Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries
よくVue.jsはSPA(Single-Page Applications)用のフレームワークだと目にしますが、Vue.js単体ではSPAは実現できなかったんですね。View層を担うVue.jsと、他のライブラリを組み合わせることでSPAが実現可能になるみたいです。ここ勘違いしてました。
参照
Declarative Rendering
The official guide assumes intermediate level knowledge of HTML, CSS, and JavaScript. If you are totally new to frontend development, it might not be the best idea to jump right into a framework as your first step - grasp the basics then come back!
HTMLやCSS、JavaScriptの基本がわかってないような輩は出直して来い!とのことですが、ごめんなさい無視します。わからなくなった都度確認するスタイルで行きます。
Vue.jsを試す一番簡単な方法は、Hello World Exampleを試すとのことで、加筆修正したスクリプトを以下に記載します。
<!DOCTYPE html> <html> <head> <title>My first Vue app</title> <script src="https://unpkg.com/vue"></script> </head> <body> <div id="app"> {{ message }} </div> <div id="app-2"> <span v-bind:title="title"> Hover your mouse over me for a few seconds to see my dynamically bound title! </span> </div> <script> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) var app2 = new Vue({ el: '#app-2', data: { title: 'You loaded this page on ' + new Date().toLocaleString() } }) </script> </body> </html>
上記ではまず、head
タグに含まれているscript
タグで、unpkg.comのCDNからVue.jsのソースコードを取得しています。
At the core of Vue.js is a system that enables us to declaratively render data to the DOM using straightforward template syntax
DOM、厳密に何なのかはわかっていなかった用語なので調べてみます。MDNに良い表現があったので引用します。
The Document Object Model (DOM) is a programming interface for HTML and XML documents. It represents the page so that programs can change the document structure, style, and content.
DOMとは、プログラムでドキュメントの構造やスタイル、内容を変更できるようなページを表現するための、プログラミングインターフェイスです。Vue.jsの例で言うと、{{ message }}
がインターフェイスとなって、プログラム側で内容を制御できるようになるわけですね。Vue.jsでは、{{ message }}
は宣言的レンダリング(Declarative Rendering)を行うためのテンプレートとされています。message
には、app
という変数名で生成したVueインスタンスのdata
オブジェクト内にある、message
の値がレンダリングされます。なので、ブラウザのJavaScriptコンソールからapp.message = 'Awesome'
のように値を変更すれば、ブラウザ画面上のテキストもAwesome
へ更新される仕組みなっています。
プロパティtitle
がレンダリングする先は、span v-bind:title="title"
というHTMLタグの属性となっていますが、v-bind属性はVue.jsによって提供された特別な属性、ディレクティブ(directive)の一つです。上のスクリプトだと、「title属性にapp2
のプロパティtitle
の値をレンダリングする」ということになりますね。v-bind
でレンダリングした場合も、ブラウザのJavaScriptコンソールからtitle
プロパティの値の変更は可能です。
参照
Conditionals and Loops
次は条件分岐とループです。以下のスクリプトをサンプルとします。
<!DOCTYPE html> <html> <head> <title>My first Vue app</title> <script src="https://unpkg.com/vue"></script> </head> <body> <div id="app-3"> <span v-if="seen">Now you see me</span> </div> <div id="app-4"> <ol> <li v-for="todo in todos"> {{ todo.text }} </li> </ol> </div> <script> var app3 = new Vue({ el: '#app-3', data: { seen: true } }) var app4 = new Vue({ el: '#app-4', data: { todos: [ { text: 'Learn JavaScript' }, { text: 'Learn Vue' }, { text: 'Build something awesome' } ] } }) </script> </body> </html>
今度は、v-if
とv-for
という新しいディレクティブが登場してきました。v-if
にはseen
というプロパティが対応しており、true
かfalse
の二値が入ることで、要素を消したり表示したりすることができます。このような構造(Structure)の変化を及ぼすディレクティブに対しては、CSSで定義するトランジションエフェクト(遷移効果)を別途付与することができます。
上記v-for
では、todos
というapp4
のプロパティ内の配列データをループで回し、{{ todo.text }}
をインターフェースにしてli
要素を複数作成しています。コンソールからapp4.todos.push({ text: 'New item' })
を実行すれば、新しい行を追加することも可能ですね。
Handling User Input
次はブラウザからの入力を扱うディレクティブの紹介です。以下のスクリプトをサンプルとします。
<!DOCTYPE html> <html> <head> <title>My first Vue app</title> <script src="https://unpkg.com/vue"></script> </head> <body> <div id="app-5"> <p>{{ message }}</p> <button v-on:click="reverseMessage">Reverse Message</button> </div> <div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div> <script> var app5 = new Vue({ el: '#app-5', data: { message: 'Hello Vue.js!' }, methods: { reverseMessage: function () { this.message = this.message.split('').reverse().join('') } } }) var app6 = new Vue({ el: '#app-6', data: { message: 'Hello Vue!' } }) </script> </body> </html>
To let users interact with your app, we can use the v-on directive to attach event listeners that invoke methods on our Vue instances
イベントリスナーとは、ブラウザからのクリックや入力といったイベントを追跡して、イベントに応じて特定の処理(関数)を実行するための仕組みのことです。通常は、要素に対してaddEventListener()
メソッドを用いることで、対象の要素でイベントを検知できるようになるのですが、そのあたりのJavaScriptのコードをVue.jsではv-on
ディレクティブとVueインスタンスでラッピングして使いやすくしているのです。div id="app-5"
要素がクリックされると、v-on:click
に紐づけられたreverseMessage
が起動し、message
データの加工を行います。reverseMessage
関数は、Vueインスタンスのmethods
というオブジェクトの中で定義され、reverseMessage関数の中では、this
でVueインスタンス自身のmessage
を呼んできています。このthis
自体は奥が深そうですが、アロー関数とやらで定義してしまうと、this
でdata
オブジェクト内のプロパティを取得できなくなってしまうみたいです。詳しくは参照のリンクから。
v-model
ディレクティブでは、インプットとアプリケーションステートを繋げる双方向バインディング(two-way binding)を実現できます。双方向バインディングでは、上の例ではフォームに入力した文字を検知して、data
オブジェクトのmessage
の値を更新してくれます。JavaScriptのMutationObserverなどで実装していくのと比べれば、かなり簡単ですね。
参照
- EventTarget.addEventListener() - Web API | MDN
- インスタンス内において、アロー関数の「this」はインスタンスを参照しない - nayucolony
- JavaScriptの「this」は「4種類」?? - Qiita
Composing with Components
Vue.jsで重要な概念の一つに、コンポーネントシステムというものがあります。「小さく、自己完結的で、再利用可能なコンポーネント」を組み合わせて実装していくのがVue.jsのお作法となります。コンポーネントの中には、データを受ける引数的な役割のprops
やHTMLデータのtemplate
を記述でき、以下の例ではprops
のtodo
属性にgroceryList
の要素をv-bind
することで、アイテムをリスト表示しています。コンポーネントは、exportとimportを駆使して他のコンポーネントからでも使えるようにすることで、規模が大きく、機能が豊富なWebアプリケーションを構築するときにより力を発揮します。
<!DOCTYPE html> <html> <head> <title>My first Vue app</title> <script src="https://unpkg.com/vue"></script> </head> <body> <div id="app-7"> <ol> <todo-item v-for="item in groceryList" v-bind:todo="item" v-bind:key="item.id" ></todo-item> </ol> </div> <script> Vue.component('todo-item', { props: ['todo'], template: '<li>{{ todo.text }}</li>' }) var app7 = new Vue({ el: '#app-7', data: { groceryList: [ { id: 0, text: 'Vegetables' }, { id: 1, text: 'Cheese' }, { id: 2, text: 'Whatever else humans are supposed to eat' } ] } }) </script> </body> </html>
Relation to Custom Elements
Vue components are very similar to Custom Elements, which are part of the Web Components Spec.
Web Components Specという単語が出てきました。Web Components Specとは、JavaScriptが提供する公式のコンポーネント機能であり、Custom Elements、Shadow DOM、HTML Templatesの3つの技術から構成されています。Vue.jsやReactがなくても、コンポーネントベースのWebサイトは実装できてしまう、ということですね。
Custom Elements(カスタム要素)はWeb Components Specの一つであり、その名の通り、自分で好きな要素を定義して使用することができるAPIで群、以下のように属性を引数的な感じに使用できます。確かにVue.jsのコンポーネントの仕様と似ていますね。
<script> class TimeFormatted extends HTMLElement { // (1) connectedCallback() { let date = new Date(this.getAttribute('datetime') || Date.now()); this.innerHTML = new Intl.DateTimeFormat("default", { year: this.getAttribute('year') || undefined, month: this.getAttribute('month') || undefined, day: this.getAttribute('day') || undefined, hour: this.getAttribute('hour') || undefined, minute: this.getAttribute('minute') || undefined, second: this.getAttribute('second') || undefined, timeZoneName: this.getAttribute('time-zone-name') || undefined, }).format(date); } } customElements.define("time-formatted", TimeFormatted); // (2) </script> <!-- (3) --> <time-formatted datetime="2019-12-01" year="numeric" month="long" day="numeric" hour="numeric" minute="numeric" second="numeric" time-zone-name="short" ></time-formatted>
(出典:Custom elements)
For example, Vue components implement the Slot API and the is special attribute.
例で上げられているSlot APIとは、HTML Templatesで用意されている要素の一つで、slot
を使用するとテンプレートで定義したテキストを変更することが可能です。ただし、これらのWeb Components Specの機能はまだ未対応のブラウザもあるため、Vue.jsではWeb Components Specに対応していないブラウザでもコンポーネントが機能するように設計されています。また、Vue.jsのコンポーネントシステムは、Web Components Specにはない以下のような機能も追加されています。
- most notably cross-component data flow
- custom event communication
- build tool integrations
この辺りは後の章で触れられそうなので今回はスキップします。
参照
- Web Components | MDN
- ライブラリを使わずここまでできる!Web Componentsで近未来のフロントエンド開発 | 株式会社ヌーラボ(Nulab inc.)
- Using templates and slots - Web Components | MDN
おわりに
Vue.jsの手軽さがよくわかるイントロダクションでしたね。Vue.jsを切り口に、JavaScriptの仕様が結構わかってきたのも嬉しいです。
前回
次回
Comming Soon!